﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using STPlatform.UPracticalMSSQLDatabaseV2;

namespace SimpleORM
{

    public class KeyValue<TKey, TValue> : CDObject
    {
        #region Properties


        public TKey Key { get; set; }

        public TValue Value { get; set; }

        #endregion

        #region Ctor

        public KeyValue()
        {
        }

        public KeyValue(TKey key, TValue value)
        {
            Key = key;
            Value = value;
        }

        #endregion

        #region Methods

        #region Methods - Override

        public override string ToString()
        {
            return string.Format("{0} : {1}", Key, Value);
        }

        #endregion

        #endregion
    }

    public class KeyValueList<TKey, TValue> : CDObjectList<KeyValue<TKey, TValue>>
    {
        #region Properties

        public virtual TValue this[TKey key]
        {
            get { return GetValue(key); }
            set { SetValue(key, value); }
        }

        #endregion

        #region Ctor

        public KeyValueList()
        {
        }

        #endregion

        #region Methods

        #region Methods - Public

        //public void Add(TKey key, TValue value)
        //{
        //    Add(new KeyValue<TKey, TValue>(key, value));
        //}

        public bool ContainsKey(TKey key)
        {
            return this.Any(c => c.Key.Equals(key));
        }

        public void Remove(TKey key)
        {
            this.RemoveAt(this.FindIndex(c => c.Key.Equals(key)));
        }

        #endregion

        #region Methods - Private

        private TValue GetValue(TKey key)
        {
            var pair = this.Where(c => c.Key.Equals(key)).FirstOrDefault();
            if (pair == null)
            {
                pair = new KeyValue<TKey, TValue>() { Key = key };
                Add(pair);
            }

            return pair.Value;
        }

        private void SetValue(TKey key, TValue value)
        {
            var pair = this.Where(c => c.Key.Equals(key)).FirstOrDefault();
            if (pair == null)
            {
                pair = new KeyValue<TKey, TValue>() { Key = key };
                Add(pair);
            }

            pair.Value = value;
        }

        #endregion

        #endregion
    }
    public interface IDCommand : ICD
    {
        #region Properties

        string CommandText { get; set; }
        int CommandTimeout { get; set; }
        object Argument { get; set; }
        IList Parameters { get; }


        #endregion

        #region Methods

        #region Methods - Execute

        object Execute();

        #endregion

        #endregion
    }
    public enum eCommandType
    {
        Select = 1,
        Count = 2,
        Query = Select | Count,
        Insert = 4,
        Delete = 8,
        Update = 16,
        Edit = Insert | Delete | Update,
    }
    public interface ICommandContext : ICD
    {
        #region Properties

        DParameterCollection Parameters { get; }
        StringBuilder CommandText { get; set; }
        eCommandType? Type { get; set; }
        object ExecuteArgument { get; set; }

        #endregion

        #region Methods

        #region Methods - Compile

        void Compile();

        #endregion

        #region Methods - Parameter

        DParameter CreateParameter();
        DParameter CreateParameter(object paramValue);
        DParameter CreateParameter(string paramName, object paramValue);
        DParameter CreateParameter(string paramName, object paramValue, object paramType);

        #endregion

        #endregion
    }
    public class NameableObject : CDObject, IComparable
    {
        #region Properties

        //        [DataMember]
        public virtual string Name { get; set; }

        #endregion

        #region Methods

        public override string ToString()
        {
            return Name;
        }

        public virtual int CompareTo(object obj)
        {
            NameableObject objName = obj as NameableObject;
            if (objName == null)
                return 0;

            return Name.CompareTo(objName.Name);
        }

        public override void Dispose()
        {
            base.Dispose();

            Name = null;
        }

        #endregion
    }
    public class DParameter : NameableObject
    {
        #region Properties

        public virtual string ParameterName { get; set; }

        public virtual object Value { get; set; }

        public virtual object Type { get; set; }

        public virtual int Size { get; set; }

        public virtual ParameterDirection Direction { get; set; }

        public string ValueString
        {
            get { return OnGetValueString(); }
        }

        #endregion

        #region Methods

        #region Methods - Virtual

        protected virtual string OnGetValueString()
        {
            return Value == null ? "NULL" : Value.ToString();
        }

        #endregion

        #region Methods - Override

        public override string ToString()
        {
            return string.Format("{0}({1}): {2} {3}", Name, ParameterName, ValueString, Type);
        }

        #endregion

        #endregion
    }
    public class CDObjectList<T> : List<T>, ICD
    {
        #region Methods

        public virtual object Clone()
        {
            IList list = Activator.CreateInstance(this.GetType()) as IList;
            MethodInfo addMethod = list.GetType().GetMethod("Add");

            foreach (T item in this)
                addMethod.Invoke(list, new object[] { CDObject.TryClone(item) });

            return list;
        }

        public virtual void Dispose()
        {
            //foreach (T item in this)
            //{
            //    IDisposable d = item as IDisposable;
            //    if (d == null)
            //        continue;

            //    d.Dispose();
            //}

            //Clear();
        }

        #endregion
    }
    public class DParameterCollection : CDObjectList<DParameter>
    {
        #region Methods

        #endregion
    }

    public class CDObjectDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ICD
    {
        #region Methods

        public virtual object Clone()
        {
            Dictionary<TKey, TValue> list = Activator.CreateInstance(this.GetType()) as Dictionary<TKey, TValue>;

            foreach (KeyValuePair<TKey, TValue> pair in this)
                list.Add(pair.Key, (TValue)CDObject.TryClone(pair.Value));

            return list;
        }

        public virtual void Dispose()
        {
            //foreach (KeyValuePair<TKey, TValue> pair in this)
            //{
            //    IDisposable d = pair.Value as IDisposable;
            //    if (d == null)
            //        continue;

            //    d.Dispose();
            //}

            //Clear();
        }

        #endregion
    }
    public class PropertyContext : CDObject
    {
        #region Properties

        public PropertyInfo PropertyInfo { get; set; }
        public DataColumnAttribute DataColumn { get; set; }

        #endregion

        #region Methods

        #region Methods - System

        public override void Dispose()
        {
            base.Dispose();

            PropertyInfo = null;
            DataColumn = null;
        }

        public override string ToString()
        {
            return string.Format("{0} -> {1}", PropertyInfo.Name, DataColumn.ColumnName);
        }

        #endregion

        #endregion
    }
    public class PropertyContextCollection : CDObjectDictionary<string, PropertyContext>
    {
    }
    public class ObjectContext : CDObject
    {
        #region Properties

        #region Properties - Map

        public PropertyContextCollection Columns { get; private set; }

        #endregion

        #region Properties - Element

        public Type ElementType { get; private set; }
        public string TableName { get; private set; }
        public string Schema { get; private set; }
        public bool IsClass { get; private set; }
        public bool IsAnonymousClass { get; private set; }

        #endregion

        #endregion

        #region Fields

        private static Hashtable htOC = new Hashtable();

        #endregion

        #region Ctor

        private ObjectContext()
        {
        }

        #endregion

        #region Methods

        #region Methods - Static

        public static ObjectContext Create(Type elementType)
        {
            lock (htOC)
            {
                if (!htOC.ContainsKey(elementType))
                    new ObjectContext().Install(elementType);

                return (htOC[elementType] as ObjectContext);
            }
        }

        #endregion

        #region Methods - Install

        private void Install(Type elementType)
        {
            IsClass = !elementType.IsValueTypeOrString();

            ConstructorInfo ci = elementType.GetConstructor(new Type[0]);

            //bool ds = elementType.IsKindOf(typeof(DynamicClass));
            //bool nc = elementType.GetField("__DynamicAnonymousClassFlag__") != null;

            IsAnonymousClass = ci == null;

            DataTableAttribute tableAttr = elementType.GetAttribute<DataTableAttribute>();
            if (tableAttr == null)
                tableAttr = new DataTableAttribute();

            TableName = string.IsNullOrWhiteSpace(tableAttr.TableName) ?
                        elementType.Name : tableAttr.TableName;
            Schema = tableAttr.Schema;

            Columns = new PropertyContextCollection();

            if (IsClass)
                elementType.TraversalPropertiesInfo(
                    DataColumnHandler, BindingFlags.Instance | BindingFlags.Public);

            ElementType = elementType;

            htOC[elementType] = this;
        }

        private bool DataColumnHandler(PropertyInfo pi)
        {
            DataColumnAttribute columnAttr = pi.GetAttribute<DataColumnAttribute>();
            if (columnAttr == null)
                columnAttr = new DataColumnAttribute();

            if (columnAttr.ColumnType == eDataType.Object)
                columnAttr.ColumnType = DotNetTypeAttribute.GetType(pi.PropertyType);

            if (!columnAttr.Enabled)
                return true;

            columnAttr.ColumnName = string.IsNullOrWhiteSpace(columnAttr.ColumnName) ?
                                    pi.Name : columnAttr.ColumnName;

            Columns[pi.Name] = new PropertyContext() { DataColumn = columnAttr, PropertyInfo = pi };

            return true;
        }

        #endregion

        #region Methods - System

        public override object Clone()
        {
            ObjectContext map = base.Clone() as ObjectContext;
            map.Columns = Columns.Clone() as PropertyContextCollection;

            return map;
        }

        public override string ToString()
        {
            return string.Format("{0} -> {1}", ElementType.Name, TableName);
        }

        #endregion

        //#region Methods - Load Objects

        //public IList Load(List<Dictionary<string, object>> listValues, TypeConverter ct)
        //{
        //    Type listType = typeof(CDObjectList<>).MakeGenericType(ElementType);
        //    IList list = (IList)Activator.CreateInstance(listType);
        //    MethodInfo addMethod = listType.GetMethod("Add");

        //    foreach (Dictionary<string, object> values in listValues)
        //        addMethod.Invoke(list, new object[] { CreateObject(values, ct) });

        //    return list;
        //}

        //private object CreateObject(Dictionary<string, object> values, TypeConverter ct)
        //{
        //    if (!IsClass)
        //        return GetValue(values.Values.ToArray(), ct);

        //    if (IsAnonymousClass)
        //        return CreateValue(values.Values.ToArray(), ct);

        //    return CreateValue(values, ct);
        //}

        //private object GetValue(object[] values, TypeConverter ct)
        //{
        //    return ct.ConvertTo(values[0], ElementType);
        //}

        //private object CreateValue(object[] values, TypeConverter ct)
        //{
        //    ParameterInfo[] pis = ElementType.GetConstructors()[0].GetParameters();
        //    for (int i = 0; i < values.Length; i++)
        //        values[i] = ct.ConvertTo(values[i], pis[i].ParameterType);

        //    return Activator.CreateInstance(ElementType, values);
        //}

        //private object CreateValue(Dictionary<string, object> values, TypeConverter ct)
        //{
        //    object rntObj = Activator.CreateInstance(ElementType);

        //    foreach (var col in values)
        //    {
        //        try
        //        {
        //            PropertyContext pc = Columns.Values.Where(
        //                p => p.PropertyInfo.Name == col.Key).FirstOrDefault();
        //            if (pc == null)
        //                continue;

        //            object val = ct.ConvertTo(col.Value, pc.PropertyInfo.PropertyType);

        //            pc.PropertyInfo.SetValue(rntObj, val, null);
        //        }
        //        catch (Exception ex)
        //        {
        //            string msg = string.Format(new EdProvider().
        //                GetName(EdProvider.langSetColumnValueFail), col.Key, ex);

        //            Tracker.WriteLine(new TrackerObject()
        //            {
        //                Description = msg,
        //                EventID = EdProvider.langSetColumnValueFail,
        //                Grade = eMessageGrade.Exception,
        //                Source = this.GetType().FullName,
        //            });
        //        }

        //    }

        //    return rntObj;
        //}

        //#endregion

        #endregion
    }
}
